package com.caird.pmp.iot.server;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


@Component
public class BbChannelManager extends ChannelInitializer<SocketChannel> {

    private static final String prefix = "bb-";

    //SIM卡号
    private static final AttributeKey<String> SIM_NO = AttributeKey.newInstance(prefix + "simNo");

    //流水号
    private static final AttributeKey<Short> FLOW_NO = AttributeKey.newInstance(prefix + "flowNo");

    //影子版本
    private static final AttributeKey<Long> VERSION = AttributeKey.newInstance(prefix + "version");

    //影子子版本
    private static final AttributeKey<Byte> SUB_VERSION = AttributeKey.newInstance(prefix + "subVersion");

    @Autowired
    private BbHandler bbHandler;

    //Channel表
    private Map<String, Channel> channelMap = new ConcurrentHashMap<>();

    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        pipeline.addLast(new BbFrameDecoder());
        pipeline.addLast(new BbFrameEncoder());
        pipeline.addLast(new IdleStateHandler(300, 0, 0, TimeUnit.SECONDS));
        pipeline.addLast(bbHandler);
    }

    /**
     * 添加Channel
     */
    public void put(String simNo, Channel channel) {
        channel.attr(SIM_NO).set(simNo);
        channelMap.put(simNo, channel);
    }

    /**
     * 获取Channel
     */
    public Channel get(String simNo) {
        return channelMap.get(simNo);
    }

    /**
     * 获取Channel
     */
    public Channel get(Channel channel) {
        String simNo = channel.attr(SIM_NO).get();
        if (null == simNo)
            return null;
        Channel channelInMap = get(simNo);
        if (channelInMap != null && channelInMap.id() == channel.id())
            return channel;
        else
            return null;
    }

    /**
     * 删除Channel
     */
    public void remove(String simNo) {
        channelMap.remove(simNo);
    }

    /**
     * 删除Channel
     */
    public void remove(Channel channel) {
        String simNo = channel.attr(SIM_NO).get();
        if (null != simNo) {
            Channel channelInMap = get(simNo);
            if (channelInMap != null && channelInMap.id() == channel.id())
                remove(simNo);
        }
    }

    /**
     * 登录
     */
    public void login(Channel channel, String simNo) {
        put(simNo, channel);
    }

    /**
     * 登出
     */
    public void logout(Channel channel) {
        remove(channel);
    }

    /**
     * 获取登录状态
     */
    public boolean isLogin(Channel channel) {
        return null != get(channel);
    }

    /**
     * 获取登录ID
     */
    public static String getLoginId(Channel channel) {
        return channel.attr(SIM_NO).get();
    }

    /**
     * 流水号递增
     */
    public static short getFlowNo(Channel channel) {
        Attribute<Short> flowNoAttr = channel.attr(FLOW_NO);
        Short flowNo = flowNoAttr.get();
        if (flowNo == null) {
            flowNo = 0;
        } else {
            flowNo++;
        }
        flowNoAttr.set(flowNo);
        return flowNo;
    }

    /**
     * 影子版本
     */
    public static long getShadowVersion(Channel channel, long newVersion) {
        Attribute<Long> versionAttr = channel.attr(VERSION);
        Long version = versionAttr.get();
        if (null == version)
            version = new Long(0);

        Attribute<Byte> subVersionAttr = channel.attr(SUB_VERSION);
        Byte subVersion = subVersionAttr.get();
        if (null == subVersion)
            subVersion = new Byte((byte)0);

        if (newVersion > version) {
            versionAttr.set(newVersion);
            subVersionAttr.set((byte)0);
            return newVersion * 1000;
        } else if (newVersion == version) {
            subVersionAttr.set(++subVersion);
            return newVersion * 1000 + subVersion;
        } else {
            return newVersion * 1000;
        }
    }
}
